﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ArgsHandler
{

    /// <summary>
    /// The ArgumentMananger class in the main class within the library.
    /// Using it you can easily handle the argument collection of your command line application.
    /// </summary>
    public class ArgumentManager
    {

        #region Public members

        /// <summary>
        /// Executes when the parsing is over.
        /// </summary>
        public event EventHandler<ParsingFinishedEventArgs> ParsingFinished;

        /// <summary>
        /// Contains the list of the current loaded arguments.
        /// </summary>
        public IEnumerable<Argument> Arguments {
            get {
                return (IEnumerable<Argument>)arguments;
            }
        }

        /// <summary>
        /// The prefix character is the character that defines the existence of a prefix, otherwise further information will be treated as the data for the main argument.
        /// e.g: -ip 127.0.0.1
        /// The prefix character is the minus (-) symbol.
        /// </summary>
        public char PrefixCharacter { get; set; } = '-';

        /// <summary>
        /// Tells the ArgumentHandler if prefixes will be found within the arguments. See PrefixCharacter property for more details.
        /// If ContainsPrefixes is false, the arguments will be separated by any empty space ( ) symbol.
        /// </summary>
        public bool UsePrefixes { get; set; } = true;

        /// <summary>
        /// Tells the ArgumentHandler whether or not is should only take in account the first occurence of an prefix and ignore the others or add all to the list.
        /// </summary>
        public bool IgnorePrefixDuplicates { get; set; } = true;

        #endregion

        #region Private members

        private string[] args;
        private List<Argument> arguments = new List<Argument>();

        #endregion

        #region Methods

        /// <summary>
        /// Constructor for the ArgumentManager.
        /// </summary>
        /// <param name="args">The command line arguments of the console application.</param>
        public ArgumentManager(string[] args)
        {
            this.args = args;
        }

        /// <summary>
        /// Handles all the arguments using the manager settings.
        /// </summary>
        /// <returns>
        /// The result of the operation.
        /// True = Successful parsing
        /// False = Failed parsing
        /// </returns>
        public bool Parse()
        {
            bool result = true;

            // No arguments failure.
            if (args.Length == 0)
            {
                result = false;
                OnParsingFinished(new ParsingFinishedEventArgs(result));
                return result;
            }

            bool onStreak = false;
            string currentData = string.Empty;
            string currentPrefix = string.Empty;
            bool continueUntilNextPrefix = false;

            // Iterates through arguments
            for (int index = 0; index < args.Length; index++)
            {            
                // If the parsing is done without prefixes, split all and add as arguments
                if(UsePrefixes == false)
                {
                    foreach (var argm in args)
                        arguments.Add(new Argument(string.Empty, argm));
                    break;
                }   
                
                // Found a prefix keyword
                if(args[index].StartsWith(PrefixCharacter.ToString()))
                {
                    // Removes the prefix character
                    currentPrefix = args[index].Remove(0, 1);

                    // Ignores an already existing prefix if necessary
                    if (IgnorePrefixDuplicates && arguments.Any(p => p.Prefix == currentPrefix))
                    {
                        continueUntilNextPrefix = true;
                        continue;
                    }
                    if (continueUntilNextPrefix)
                        continueUntilNextPrefix = false;

                    // If there's no data next, don't create streak
                    if (index == args.Length - 1 || args[index + 1].StartsWith(PrefixCharacter.ToString()))
                    {
                        arguments.Add(new Argument(currentPrefix, string.Empty));
                        continue;
                    }
             
                    onStreak = true; // Streak = we continue parsing until a new prefix is found.

                    // When it is found, we use currentData and currentPrefix to create the argument and move on.

                }
                else // Found data
                {
                    if (continueUntilNextPrefix)
                        continue;

                    if(onStreak)
                    {
                        // If last in collection just create the argument and add it
                        if(index == args.Length - 1 || args[index + 1].StartsWith(PrefixCharacter.ToString()))
                        {
                            currentData += args[index];
                            arguments.Add(new Argument(currentPrefix, currentData));

                            // Reset streak
                            onStreak = false;
                            currentPrefix = string.Empty;
                            currentData = string.Empty;

                            continue;
                        }
                        else
                        {
                            currentData += args[index] + ' ';
                        }

                    }
                    else // Happens when data is found first, there's no prefix and the UsePrefixes is on.
                    {
                        result = false;
                        // Raises the finished parsing event.
                        OnParsingFinished(new ParsingFinishedEventArgs(result));
                        return result;
                    }
                }

            }

            // Raises the finished parsing event.
            OnParsingFinished(new ParsingFinishedEventArgs(result));

            return result;
        }
        public bool Parse(bool UsePrefixes = true)
        {
            this.UsePrefixes = UsePrefixes;
            return Parse();
        }
        public bool Parse(bool UsePrefixes = true, char PrefixCharacter = '-')
        {
            this.PrefixCharacter = PrefixCharacter;
            return Parse(UsePrefixes);
        }
        public bool Parse(bool UsePrefixes = true, char PrefixCharacter = '-', bool IgnorePrefixDuplicates = true)
        {
            this.IgnorePrefixDuplicates = IgnorePrefixDuplicates;
            return Parse(UsePrefixes, PrefixCharacter);
        }

        protected virtual void OnParsingFinished(ParsingFinishedEventArgs e)
        {
            if (ParsingFinished != null)
                ParsingFinished(this, e);
        }

        public class ParsingFinishedEventArgs : EventArgs
        {
            public ParsingFinishedEventArgs(bool result) => Result = result;

            public bool Result { get; set; }
        }

        #endregion

    }
}
